package com.hb.core.pojo.parent;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.fzzn.core.util.IdUtil;
import org.springframework.format.annotation.DateTimeFormat;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Getter
@Setter
@ToString
public class MPCommonEntity extends MPPageEntity {

    /**
     * 父类冗余字段
     * 用于增加mybatisplus注解
     */
    @TableId(type = IdType.INPUT)
    private Long id;

    /**
     * 创建日期
     */
    @ApiModelProperty(hidden = true)
    // @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
    // @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    /**
     * 创建人ID
     */
    @ApiModelProperty(hidden = true)
    private Long createId;

    /**
     * 创建人名称
     */
    @ApiModelProperty(hidden = true)
    private String createName;

    /**
     * 修改日期
     */
    @ApiModelProperty(hidden = true)
    @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

    /**
     * 修改人ID
     */
    @ApiModelProperty(hidden = true)
    private Long updateId;

    /**
     * 修改人名称
     */
    @ApiModelProperty(hidden = true)
    private String updateName;

    /**
     * 删除标志(0: 有效, 1: 已删除)
     */
    @ApiModelProperty(hidden = true)
    private Integer deleteFlag;

    /**
     * 填充基础数据
     * 填充包含属性:id, createDate, updateDate, createName, updateName, deleteFlag
     *
     * @param type 1: 新增需要的数据, 2: 修改需要的数据, 3: 删除
     * @param name 当前登陆的用户姓名
     */
    public void buildBaseData(int type, String name, Long operateId) {
        switch (type) {
            case 1:
                if (this.id == null) {
                    this.setId(IdUtil.getId());
                }
                this.setCreateTime(LocalDateTime.now());
                if (StrUtil.isEmpty(this.getCreateName())) {
                    this.setCreateName(name);
                }
                if (this.getCreateId() == null) {
                    this.setCreateId(operateId);
                }
                this.setDeleteFlag(0);
                break;
            case 3:
                this.setDeleteFlag(1);
            case 2:
                this.setUpdateTime(LocalDateTime.now());
                this.setUpdateId(operateId);
                if (StrUtil.isEmpty(this.getUpdateName())) {
                    this.setUpdateName(name);
                }
                break;
            default:
        }
    }

    public void buildBaseData(int type, String name) {
        buildBaseData(type, name, null);
    }

    public void buildBaseData(int type) {
        String realName = "SYSTEM";
        buildBaseData(type, realName);
    }

    /**
     * 根据当前对象中是否拥有id属性判断设置基础数据
     *
     * @param name 当前登陆的用户姓名
     */
    public void buildBaseData(String name) {
        if (this.id != null) {
            buildBaseData(2, name);
            return;
        }
        buildBaseData(1, name);
    }

    /**
     * 将当前父类对象转换为相应的子类对象
     *
     * @param cls
     * @param <A>
     * @return
     * @see CopyOperation
     * @deprecated 该方法转移至接口中调用
     */
    @Deprecated
    public <A extends MPCommonEntity> A to(Class<A> cls) {
        A childEntity = ReflectUtil.newInstanceIfPossible(cls);
        return to(childEntity);
    }

    /**
     * 将当前父类对象转换为相应的子类对象
     *
     * @param entity 子类类型
     * @param <A>
     * @return
     * @see CopyOperation
     * @deprecated 该方法转移至接口中调用
     */
    @Deprecated
    public <A extends MPCommonEntity> A to(A entity) {
        BeanUtil.copyProperties(this, entity, CopyOptions.create().ignoreNullValue());
        return entity;
    }

    /**
     * 将实体类集合转换为指定的类型集合
     * 该方法新建了集合和元素对象
     * 不建议用于数据量过大的操作
     *
     * @param list
     * @param clazz
     * @param <A>
     * @return
     */
    public static <A extends MPCommonEntity> List<A> to(List<? extends MPCommonEntity> list, Class<A> clazz) {
        List<A> newList = CollUtil.list(false);
        for (MPCommonEntity mpCommonEntity : list) {
            newList.add(mpCommonEntity.to(clazz));
        }
        return newList;
    }

    /**
     * 构建QueryWrapper对象
     * 添加删除标志
     * 附带sql语句注释
     *
     * @param selectFields
     * @param <A>
     * @return
     */
    public static <A extends MPCommonEntity> QueryWrapper<A> wrapperComment(String comment, String... selectFields) {
        if (StrUtil.isEmpty(comment)) {
            throw new RuntimeException("com.hb.core.pojo.parent.MPCommonEntity.wrapperComment方法的调用必须添加注释");
        }
        QueryWrapper<A> wrapper = wrapper(selectFields);
        wrapper.comment(comment);
        return wrapper;
    }

    /**
     * 构建QueryWrapper对象
     * 添加删除标志
     *
     * @param selectFields 字段支持驼峰或者下划线都行
     * @param <A>
     * @return
     */
    public static <A extends MPCommonEntity> QueryWrapper<A> wrapper(String... selectFields) {
        QueryWrapper<A> wrapper = wrapperAll(selectFields);
        wrapper.eq("delete_flag", 0);
        return wrapper;
    }

    /**
     * 构建LambdaQueryWrapper对象
     * 添加删除标志
     *
     * @param selectFields
     * @param <A>
     * @return
     */
    public static <A extends MPCommonEntity> LambdaQueryWrapper<A> lambdaWrapper(final SFunction<A, ?>... selectFields) {
        final LambdaQueryWrapper<A> lambdaQuery = Wrappers.lambdaQuery();
        if (ArrayUtil.isNotEmpty(selectFields)) {
            lambdaQuery.select(selectFields);
        }
        lambdaQuery.eq(MPCommonEntity::getDeleteFlag, 0);
        return lambdaQuery;
    }

    /**
     * 构建QueryWrapper对象
     * 没有删除标志判断, 会查询所有数据
     *
     * @param selectFields 字段支持驼峰或者下划线都行
     * @param <A>
     * @return
     */
    public static <A extends MPCommonEntity> QueryWrapper<A> wrapperAll(String... selectFields) {
        QueryWrapper<A> query = Wrappers.query();
        if (ArrayUtil.isNotEmpty(selectFields)) {

            // 将字段中的驼峰命名转换为下划线 start
            String[] underLineFields = new String[selectFields.length];
            int index = 0;
            for (String selectField : selectFields) {
                underLineFields[index++] = StrUtil.toUnderlineCase(selectField);
            }
            // 将字段中的驼峰命名转换为下划线 end

            query.select(underLineFields);
        }
        return query;
    }

    /**
     * 将当前对象转换成可查询用的Wrapper
     * String类型为like
     * 日期对比的精度为一天
     * 其他为等于
     *
     * @param <A>
     * @return
     * @deprecated 该方法不建议使用, 查询条件不够细化
     * FIXME 如果实体类中只包含整型的参数可以使用, 后期修改
     */
    @Deprecated
    public <A extends MPCommonEntity> QueryWrapper<A> buildWrapper() {
        Field[] fields = ReflectUtil.getFields(this.getClass());
        QueryWrapper<A> wrapper = wrapper();
        for (Field field : fields) {
            String fieldName = field.getName();
            if ("serialVersionUID".equals(fieldName)) {
                continue;
            }

            // 静态资源忽略
            boolean isStatic = Modifier.isStatic(field.getModifiers());
            if (isStatic) {
                continue;
            }

            // 分页参数字段不参与查询
            Field pageField = ReflectUtil.getField(MPPageEntity.class, fieldName);
            if (pageField != null) {
                continue;
            }

            // 驼峰转下划线
            String underLineFieldName = StrUtil.toUnderlineCase(fieldName);
            Object fieldValue = ReflectUtil.getFieldValue(this, field);
            if (fieldValue == null) {
                continue;
            }
            if (StrUtil.isEmpty(fieldValue.toString())) {
                continue;
            }
            if (fieldValue instanceof String) {
                wrapper.like(underLineFieldName, fieldValue);
            } else if (fieldValue instanceof Date) {
                String dateStr = DateUtil.formatDate((Date) fieldValue);
                wrapper.eq(underLineFieldName, dateStr);
            } else if (fieldValue instanceof LocalDateTime) {
                String dateStr = DateUtil.formatLocalDateTime((LocalDateTime) fieldValue);
                wrapper.eq(underLineFieldName, dateStr);
            } else {
                wrapper.eq(underLineFieldName, fieldValue);
            }
        }

        return wrapper;
    }

    /**
     * 参数验证方法
     * 所有的继承该类的DTO对象必须在调用时重写该方法
     * 该方法中验证的字段对应数据库中不能为空的字段
     *
     * @return
     * @deprecated 改为接口实现逻辑
     * @see ParamsCheck
     */
    @Deprecated
    public String paramsValid(String... fieldNames) {
        if (ArrayUtil.isEmpty(fieldNames)) {
            return null;
        }
        for (String fieldName : fieldNames) {
            Field field = ReflectUtil.getField(this.getClass(), fieldName);
            if (field == null) {
                continue;
            }
            Object fieldValue = ReflectUtil.getFieldValue(this, field);
            if (fieldValue == null) {
                return "[" + field.getName() + "] 不能为空";
            }
            Class<?> type = field.getType();
            if (type == String.class) {
                if ("".equals(fieldValue.toString())) {
                    return "[" + field.getName() + "] 不能为空";
                }
            }
        }
        return null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        MPCommonEntity that = (MPCommonEntity) o;
        return Objects.equals(id, that.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}
